﻿//version 3: the class is created. Attributes are created. Methods: Paint,
//PaintCardinalitiesAndAttributes,PaintAttributeName,PaintCardinality,PaintName,
//PaintLine, SetIniClass, SetEndClass
//version 4: added attribute name;
//version 5: added constructor, PaintEndSymbol
//changed methods: Paint, PaintCardinalitiesAndAttributes, PaintAttributeName, PaintCardinality
//version 9: GetRelationType

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace UMLDesigner
{
   public class RelationView
    {
        protected static Pen myPen = new Pen(Color.Gray, 2);
        private static StringFormat alignRightFormat = new StringFormat();
        private static StringFormat alignCenterFormat = new StringFormat();

        protected ClassView iniClass;
        protected ClassView endClass;

        protected RelationshipData relationData = new RelationshipData();

        protected int endSymbolSize = 10;

        protected Point[] finalPolygon;

        public RelationView()
        {
            alignRightFormat.Alignment = StringAlignment.Far;
            alignCenterFormat.Alignment = StringAlignment.Center;
        }

        public void Paint(Graphics g)
        {
            Point iniP = iniClass.GetCenter(),
                  endP = endClass.GetCenter();

            int horizDistance = Math.Abs(iniP.X - endP.X);
            int horizDirection = iniP.X > endP.X ? 1 : -1;  // 1: right, -1: left
            int verticalDistance = Math.Abs(iniP.Y - endP.Y);
            int verticalDirection = iniP.Y > endP.Y ? 1 : -1;  // 1: down, -1: up

            Point endCoord = endP;
            Point lineEndCoord, iniCardinalityCoord, endCardinalityCoord;
            float rotation;

            if (verticalDistance > horizDistance)
            {
                //Paints the end of the relation at the top or at the bottom
                int dy = verticalDirection * endClass.GetHeight() / 2;
                lineEndCoord = new Point(endP.X, endP.Y + dy + verticalDirection * endSymbolSize);

                //Apply the calculated offset
                 endCoord.Offset(0, dy);
                rotation = verticalDirection == 1 ? 0f : 180f;

                //Cardinality               
                 iniCardinalityCoord = new Point(iniP.X, iniP.Y - verticalDirection * (20 + iniClass.GetHeight() / 2));
                 endCardinalityCoord = new Point(endP.X, endP.Y + verticalDirection * (20 + endClass.GetHeight() / 2));
            }
            else
            {
                //Paints the end of the relation to the left or to the right
                int dx = horizDirection * endClass.GetWidth() / 2;
                lineEndCoord = new Point(endP.X + dx + horizDirection * endSymbolSize, endP.Y);

                //Apply the calculated offset
                endCoord.Offset(dx, 0);
                rotation = horizDirection == 1 ? 270F : 90F;

                //Cardinality               
                iniCardinalityCoord = new Point(iniP.X - horizDirection * (20 + iniClass.GetWidth() / 2), iniP.Y);
                endCardinalityCoord = new Point(endP.X + horizDirection * (20 + endClass.GetWidth() / 2), endP.Y);
            }


            PaintLine(g, iniP, lineEndCoord);
            PaintName(g, iniP, endP);
            PaintCardinalitiesAndAttributes(g, iniCardinalityCoord, endCardinalityCoord, rotation);
            PaintEndSymbol(g, endCoord, rotation);
        }

        private void PaintCardinalitiesAndAttributes(Graphics g, Point iniP, Point endP, float rotation)
        {
            //Initial Cardinality and attribute name
            PaintCardinality(g, iniP, relationData.GetIniCardinality(), rotation);
            string visibility = VisibilityType.GetUMLRepresentation(relationData.GetVisibilityIniAttribute());
            PaintAttributeName(g, iniP, visibility + " " + relationData.GetAttrNameIni(), rotation);

            //Final Cardinality and attribute name
            PaintCardinality(g, endP, relationData.GetEndCardinality(), 360 - rotation);
            visibility = VisibilityType.GetUMLRepresentation(relationData.GetVisibilityEndAttribute());
            PaintAttributeName(g, endP, visibility + " " + relationData.GetAttrNameEnd(), 360 - rotation);
        }

        private void PaintAttributeName(Graphics g, Point p, string card, float rotation)
        {
            int dy = 0, dx = 0;
            StringFormat format = StringFormat.GenericDefault;
            switch ((int)rotation)
            {
                case 0:
                case 360: 
                    format = alignRightFormat; 
                    dx = -5; 
                    dy = -8; 
                    break;
                case 90: 
                    break;
                case 180: 
                    dx = 5;
                    dy = -8;
                    break;
                case 270: 
                    format = alignRightFormat; 
                    break;
            }

            Point pMod = new Point(p.X + dx, p.Y + dy);
            g.DrawString(card, SystemFonts.DefaultFont, Brushes.Black, pMod, format);
        }

        private void PaintCardinality(Graphics g, Point p, string card, float rotation)
        {
            //Paint the cardinality depends on the rotation
            int incrementY = 0, incrementX = 0;
            StringFormat format = StringFormat.GenericDefault;
            switch ((int)rotation)
            {
                case 0:
                case 360: 
                    incrementX = 20; 
                    break;
                case 90: 
                    incrementX = 20; 
                    incrementY = -10; 
                    break;
                case 180: 
                    format = alignRightFormat; 
                    incrementX = -20;
                    break;
                case 270: 
                    format = alignRightFormat; 
                    incrementX = -20; 
                    incrementY = -10; 
                    break;
            }

            Point modificatedPoint = new Point(p.X + incrementX - 20, p.Y + incrementY - 8);
            Rectangle rectangle = new Rectangle(modificatedPoint, new Size(40, 16));
            g.DrawString(card, SystemFonts.DefaultFont, Brushes.Black, rectangle, format);
        }

        private void PaintName(Graphics g, Point ini, Point fin)
        {
            Point pCentro = new Point((ini.X + fin.X) / 2, (ini.Y + fin.Y) / 2 - 15);
            g.DrawString(relationData.GetName(), SystemFonts.DialogFont, Brushes.Black, pCentro, alignCenterFormat);
        }

        private void PaintLine(Graphics g, Point ini, Point end)
        {
            g.DrawLine(myPen, ini, end);
        }

        protected virtual void PaintEndSymbol(Graphics g, Point finalCoord, float rotation)
        {
            g.TranslateTransform(finalCoord.X, finalCoord.Y);
            g.RotateTransform(rotation);
            g.FillPolygon(myPen.Brush, finalPolygon);
            g.ResetTransform();
        }

        public virtual string GetRelationType()
        {
            return "";
        }
        public void SetRelationData(RelationshipData newRelData)
        {
            relationData = newRelData;
        }

        public void SetIniClass(ClassView newClass)
        {
            iniClass = newClass;
        }

        public void SetEndClass(ClassView newClass)
        {
            endClass = newClass;
        }

        public ClassView GetIniClass()
        {
            return iniClass;
        }

        public ClassView GetEndClass()
        {
            return endClass;
        }
        public virtual RelationView CreateObject()
        {
            return null;
        }
        public RelationshipData GetRelationData()
        {
            return relationData;
        }

    }
}
